package com.ruoyi.system.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.system.service.IRedisOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisOperationImpl implements IRedisOperation {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisOperationImpl.class);
    private static final long DEFAULT_REDIS_KEYS_TIMEOUT = 604800L;

    private StringRedisTemplate stringRedisTemplate;

    public RedisOperationImpl(StringRedisTemplate template) {
        this.stringRedisTemplate = template;
    }

    @Override
    public Boolean hasKey(String key) {
        return this.stringRedisTemplate.hasKey(key);
    }

    @Override
    public void setValue(String key, String value) {
        this.setValue(key, value, DEFAULT_REDIS_KEYS_TIMEOUT);
    }

    @Override
    public void setValue(String key, String value, Long timeout) {
        this.stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    @Override
    public void setValue(String key, String value, Long timeout, TimeUnit unit) {
        this.stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    @Override
    public void setValuePermanent(String key, String value) {
        this.stringRedisTemplate.opsForValue().set(key, value);
    }

    @Override
    public String getValue(String key) {
        return (String) this.stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public String getAndSetValue(String key, String value) {
        return (String) this.stringRedisTemplate.opsForValue().getAndSet(key, value);
    }

    @Override
    public List<String> getValues(String prex) {
        Set<String> keys = this.stringRedisTemplate.keys(prex + "*");
        return this.stringRedisTemplate.opsForValue().multiGet(keys);
    }

    @Override
    public void removeV(String key) {
        this.stringRedisTemplate.delete(key);
    }

    @Override
    public void setSetValue(String key, Long timeout, String... values) {
        if (timeout == null) {
            timeout = 604800L;
        }

        this.stringRedisTemplate.opsForSet().add(key, values);
        this.stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    @Override
    public Set<String> getSetValues(String key) {
        return this.stringRedisTemplate.opsForSet().members(key);
    }

    @Override
    public Boolean isMember(String key, String value) {
        return this.stringRedisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long getSetSize(String key) {
        return this.stringRedisTemplate.opsForSet().size(key);
    }

    @Override
    public void setHashValue(String key, String hashKey, String hashValue) {
        this.stringRedisTemplate.opsForHash().put(key, hashKey, hashValue);
    }

    @Override
    public void setHashAllValue(String key, Map<String, String> var2) {
        this.stringRedisTemplate.opsForHash().putAll(key, var2);
    }

    @Override
    public void setHashValue(String key, String hashKey, String hashValue, Long timeOut) {
        this.stringRedisTemplate.opsForHash().put(key, hashKey, hashValue);
        this.stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
    }

    @Override
    public String getHashValue(String key, String hashKey) {
        return null != this.stringRedisTemplate.opsForHash().get(key, hashKey) ?
                String.valueOf(this.stringRedisTemplate.opsForHash().get(key, hashKey)) : null;
    }

    @Override
    public List getHashValues(String key) {
        return this.stringRedisTemplate.opsForHash().values(key);
    }

    @Override
    public List mutiGetHashValue(String key, List hashKeys) {
        return this.stringRedisTemplate.opsForHash().multiGet(key, hashKeys);
    }

    @Override
    public Set getHashKeys(String key) {
        return this.stringRedisTemplate.opsForHash().keys(key);
    }

    @Override
    public void removeHashEntry(String key, String... hashKey) {
        this.stringRedisTemplate.opsForHash().delete(key, hashKey);
    }

    @Override
    public void removeKey(String key) {
        this.stringRedisTemplate.delete(key);
    }

    @Override
    public void removeSetEntry(String setkey, String... setValue) {
        this.stringRedisTemplate.opsForSet().remove(setkey, setValue);
    }

    @Override
    public boolean putIfAbsent(String key, String value) {
        return this.stringRedisTemplate.opsForValue().setIfAbsent(key, value);
    }

    @Override
    public boolean putIfAbsent(String key, String value, Long timeout, TimeUnit timeUnit) {
        return this.stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
    }

    @Override
    public String batchRemoveByPrexKey(String prex) {
        if (prex != null && !"".equals(prex.trim())) {
            Set<String> keys = this.stringRedisTemplate.keys(prex + "*");
            String result = this.clearByKeys(keys);
            return result;
        } else {
            return "prex_not_empty";
        }
    }

    @Override
    public String batchRemoveByKeys(String keyListStr, String splitChar) {
        if (keyListStr != null && !"".equals(keyListStr.trim())) {
            if (splitChar == null || "".equals(splitChar.trim())) {
                splitChar = "\\|";
            }

            String[] keyList = keyListStr.split(splitChar);
            Set<String> keys = new HashSet();
            String[] var5 = keyList;
            int var6 = keyList.length;

            for (int var7 = 0; var7 < var6; ++var7) {
                String key = var5[var7];
                keys.add(key);
            }

            return this.clearByKeys(keys);
        } else {
            return "keys_not_empty";
        }
    }

    @Override
    public void increaseKey(String key, int num, Long timeout, TimeUnit timeUnit) {
        this.stringRedisTemplate.boundValueOps(key).increment((long) num);
        this.stringRedisTemplate.expire(key, timeout, timeUnit);
    }

    @Override
    public long increaseKeyValue(String key, int num) {
        return this.stringRedisTemplate.boundValueOps(key).increment((long) num);
    }

    public String clearByKeys(Set<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return "keys_not_empty";
        } else {
            Set<String> tempKeys = new HashSet();
            int count = 0;
            Iterator var4 = keys.iterator();

            while (var4.hasNext()) {
                String key = (String) var4.next();
                tempKeys.add(key);
                if (tempKeys.size() == 500) {
                    count += tempKeys.size();
                    this.stringRedisTemplate.delete(tempKeys);
                    tempKeys.clear();
                }
            }

            if (!tempKeys.isEmpty()) {
                count += tempKeys.size();
                this.stringRedisTemplate.delete(tempKeys);
                tempKeys.clear();
            }

            return "success" + count;
        }
    }

    @Override
    public void longExpire(String key, final long timeout, final TimeUnit unit) {
        this.stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    @Override
    public long getExpire(String key, TimeUnit timeUnit) {
        return this.stringRedisTemplate.getExpire(key, timeUnit);
    }

    @Override
    public String rightPop(String key) {
        return (String) this.stringRedisTemplate.opsForList().rightPop(key);
    }

    @Override
    public List<String> range(String key, long start, long end) {
        return this.stringRedisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public void listPush(String key, String value, long timeout) {
        this.stringRedisTemplate.opsForList().leftPush(key, value);
        this.stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    @Override
    public void zsetAdd(String key, String value, double score, long timeout) {
        this.stringRedisTemplate.opsForZSet().add(key, value, score);
        this.stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        long size = this.stringRedisTemplate.opsForZSet().zCard(key);
        if (size > 30L) {
            this.stringRedisTemplate.opsForZSet().removeRange(key, 0L, 0L);
        }

    }

    @Override
    public Set<String> rangeByScore(String key, double min, double max, long offset, long count) {
        return this.stringRedisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
    }

    @Override
    public Set<String> rangeByScore(String key, double min, double max) {
        return this.stringRedisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    @Override
    public Set<String> zRange(String key, long begin, long end) {
        return this.stringRedisTemplate.opsForZSet().range(key, begin, end);
    }

    @Override
    public Set<String> zrevRangeByScore(String key, double min, double max) {
        return this.stringRedisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    @Override
    public Set<String> zrevRangeByScoreLimit(String key, double min, double max, long limit) {
        return this.stringRedisTemplate.opsForZSet().reverseRangeByScore(key, min, max, 0L, limit);
    }

    @Override
    public List<String> executePipeline(final List<String> keys, final double min, final double max) {
        final RedisSerializer<String> serializer = this.stringRedisTemplate.getStringSerializer();
        List<Object> resultList = this.stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection conn) throws DataAccessException {
                Iterator var2 = keys.iterator();

                while (var2.hasNext()) {
                    String key = (String) var2.next();
                    byte[] keyname = serializer.serialize(key);
                    conn.get(keyname);
                }

                return null;
            }
        }, serializer);
        List<String> result = new ArrayList();
        Iterator var9 = resultList.iterator();

        while (var9.hasNext()) {
            Object o = var9.next();
            if (o != null) {
                result.add(String.valueOf(o));
            }
        }

        return result;
    }

    @Override
    public List<String> batchZget(final List<String> keys, final double max, final double min) {
        final RedisSerializer<String> serializer = this.stringRedisTemplate.getStringSerializer();
        List<Object> resultList = this.stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection conn) throws DataAccessException {
                Iterator var2 = keys.iterator();

                while (var2.hasNext()) {
                    String key = (String) var2.next();
                    byte[] keyname = serializer.serialize(key);
                    conn.zRevRangeByScore(keyname, min, max);
                }

                return null;
            }
        }, serializer);
        List<String> result = new ArrayList();
        Iterator var9 = resultList.iterator();

        while (var9.hasNext()) {
            Object o = var9.next();
            if (o != null) {
                result.add(String.valueOf(o));
            }
        }

        return result;
    }

    @Override
    public void batchZsetAdd(List<String> keys, List<String> values, double score, long timeout) {
        RedisSerializer<String> serializer = this.stringRedisTemplate.getStringSerializer();
        this.stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection conn) throws DataAccessException {
                for (int i = 0; i < keys.size(); ++i) {
                    String key = (String) keys.get(i);
                    String value = (String) values.get(i);
                    conn.zSetCommands().zAdd(key.getBytes(), score, value.getBytes());
                    conn.expire(key.getBytes(), timeout);
                }

                return null;
            }
        }, serializer);
    }

    @Override
    public void batchDelete(final List<String> keys) {
        final RedisSerializer<String> serializer = this.stringRedisTemplate.getStringSerializer();
        this.stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection conn) throws DataAccessException {
                Iterator var2 = keys.iterator();

                while (var2.hasNext()) {
                    String key = (String) var2.next();
                    byte[] keyname = serializer.serialize(key);
                    conn.del(new byte[][]{keyname});
                }

                return null;
            }
        }, serializer);
    }

    @Override
    public String deserialize(byte[] body) {
        return (String) this.stringRedisTemplate.getValueSerializer().deserialize(body);
    }

    @Override
    public void convertAndSend(String channel, String message) {
        this.stringRedisTemplate.convertAndSend(channel, message);
    }

    @Override
    public void publishMessage(String channel, String event, String data) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("event", event);
        jsonObject.put("data", data);
        this.stringRedisTemplate.convertAndSend(channel, jsonObject.toJSONString());
    }

    @Override
    public void produceMessage(String channel, String event, String data) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("event", event);
        jsonObject.put("data", data);
        this.stringRedisTemplate.opsForZSet().add(channel, jsonObject.toJSONString(),
                (double) System.currentTimeMillis());
    }

    @Override
    public String loadScript(RedisScript redisScript) {
        return this.stringRedisTemplate.getConnectionFactory().getConnection().scriptLoad(redisScript.getScriptAsString().getBytes());
    }

    @Override
    public void setOpsLog(String key, String... value) {
        this.stringRedisTemplate.opsForHyperLogLog().add(key, value);
    }

    @Override
    public Long opsLogSize(String key) {
        return stringRedisTemplate.opsForHyperLogLog().size(key);
    }
}
